package jus.poc.prodcons.v6;

import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.Semaphore;
import jus.poc.prodcons.*;

/**
 * @author Nunes Thomas <nds.thomas@gmail.com>
 * @author Feng Xu <fengxu1989@gmail.com>
 */
public final class Observateur {

    private boolean coherent;
    private int nbProducteurs;
    private int nbConsommateurs;
    private int nbBuffer;
    private int bufInUse;
    private ArrayList<_Consommateur> consommateurs;
    private ArrayList<_Producteur> producteurs;
    private ArrayList<Message> messages;
    private ArrayList<Date> datesP;
    private ArrayList<Date> dateDepot;
    private ArrayList<Integer> delayP;
    private ArrayList<Date> datesC;
    private Semaphore mutex = new Semaphore(1);

    public Observateur() {
        coherent = true;
        consommateurs = new ArrayList<_Consommateur>();
        producteurs = new ArrayList<_Producteur>();
        messages = new ArrayList<Message>();
        datesP = new ArrayList<Date>();
        dateDepot = new ArrayList<Date>();
        datesC = new ArrayList<Date>();
        delayP = new ArrayList<Integer>();
        bufInUse = 0;
    }

    /**
     * Teste si l'application respecte le protocole
     *
     * @return Vrai si elle respecte le protocole
     */
    public boolean coherent() {
        int i = 0;
        while (i < datesP.size()) {
            if (datesC.get(i).getTime() < dateDepot.get(i).getTime()) {
                coherent = false;
                System.out.println("--OBSERVATEUR : TS(DEPOSER(M))<TS(RETIRER(M)) n'est pas respecte");
            }
            int j = 0;
            while (j < datesP.size() && coherent) {
                if (dateDepot.get(i).getTime() < dateDepot.get(j).getTime()) {
                    if (!(datesC.get(i).getTime() <= datesC.get(j).getTime())) {
                        coherent = false;
                        System.out.println("--OBSERVATEUR : les messages ne sont pas retire dans l'ordre, conflit sur les messages " + ((MessageX) messages.get(i)).id() + " ," + ((MessageX) messages.get(j)).id());
                    }
                }
                j++;
                if (i == j) {
                    j++;
                }
            }
            i++;
        }


        return (coherent && nbConsommateurs == consommateurs.size() && nbProducteurs == producteurs
                .size());
    }

    private boolean verifConso(_Consommateur c) {
        boolean b = consommateurs.contains(c);
        if (!b) {
            coherent = false;
            System.out.println("--OBSERVATEUR : Le consommateur est inconnu");
        }
        return b;
    }

    private boolean verifMessage(Message m) {
        boolean b = messages.contains(m);
        if (!b) {
            coherent = false;
            System.out.println("--OBSERVATEUR : Le message est inconnu");
        }
        return b;
    }

    private boolean verifProd(_Producteur p) {
        boolean b = producteurs.contains(p);
        if (!b) {
            coherent = false;
            System.out.println("--OBSERVATEUR : Le producteur est inconnu");
        }
        return b;
    }

    public void consommationMessage(_Consommateur c, Message m,
            int tempsDeTraitement) throws InterruptedException {
        Date date = new Date();
        mutex.acquire();
        if (verifConso(c) && verifMessage(m)) {
            int index = messages.indexOf(m);

            if (date.getTime() - datesC.get(index).getTime() < tempsDeTraitement) {
                coherent = false;
                System.out.println("--OBSERVATEUR : Le temps de traitement de consommation n'est pas respecte");
            }
        }
        mutex.release();
    }

    public void depotMessage(_Producteur p, Message m)
            throws InterruptedException {
        Date date = new Date();
        mutex.acquire();
        if (verifProd(p) && verifMessage(m)) {
            int index = messages.indexOf(m);
            bufInUse++;
            if (date.getTime() - datesP.get(index).getTime() < delayP
                    .get(index) || bufInUse > nbBuffer) {
                coherent = false;
                if (date.getTime() - datesP.get(index).getTime() < delayP
                        .get(index)) {
                    System.out.println("--OBSERVATEUR : Le temps de traitement de production n'est pas respecte");
                } else {
                    System.out.println("--OBSERVATEUR : il y a ecrasement d'un message par " + p + " avec le " + m);
                }

            }
            dateDepot.set(index, date);
        }
        mutex.release();
    }

    public void init(int nbproducteurs, int nbconsommateurs, int nbBuffers) {
        nbProducteurs = nbproducteurs;
        nbConsommateurs = nbconsommateurs;
        this.nbBuffer = nbBuffers;
        if (nbBuffers == 0) {
            coherent = false;
            System.out.println("--OBSERVATEUR : nombre de buffers incorrect");
        }
    }

    public void newConsommateur(_Consommateur c) throws InterruptedException {
        mutex.acquire();
        if (consommateurs.contains(c)) {
            coherent = false;
            System.out.println("--OBSERVATEUR : Consommateur deja existant");
        } else {
            consommateurs.add(c);
        }
        mutex.release();
    }

    public void newProducteur(_Producteur p) throws InterruptedException {
        mutex.acquire();
        if (producteurs.contains(p)) {
            coherent = false;
            System.out.println("--OBSERVATEUR : Producteur deja existant");
        } else {
            producteurs.add(p);
        }
        mutex.release();
    }

    public void productionMessage(_Producteur p, Message m,
            int tempsDeTraitement) throws InterruptedException {
        mutex.acquire();
        if (verifProd(p)) {
            if (messages.contains(m)) {
                coherent = false;
                System.out.println("--OBSERVATEUR : Message deja produit");
            } else {
                messages.add(m);
                delayP.add(tempsDeTraitement);
                datesP.add(new Date());
                datesC.add(new Date(1));
                dateDepot.add(new Date(1));
            }
        }
        mutex.release();
    }

    public void retraitMessage(_Consommateur c, Message m)
            throws InterruptedException {
        mutex.acquire();
        if (verifConso(c) && verifMessage(m)) {
            if (bufInUse <= 0) {
                coherent = false;
                System.out.println("--OBSERVATEUR : Message inconnu (retrait d'un message sans messages dans le buffer)");
            } else {
                int index = messages.indexOf(m);
                datesC.set(index, new Date());
                bufInUse--;
            }
        }
        mutex.release();
    }
}
